﻿using Cineman.Resources;
using HtmlAgilityPack;
using Microsoft.Phone;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Media.Imaging;

namespace Cineman.ViewModels
{
    public class MovieData : INotifyPropertyChanged
    {
        #region Properties declarations

        private int _id;
        /// <summary>
        /// Movie ID
        /// </summary>
        public int ID
        {
            get { return _id; }
            set { _id = value; NotifyPropertyChanged("ID"); }
        }

        private bool _today;
        /// <summary>
        /// Defines if movie being aired today
        /// </summary>
        public bool Today
        {
            get { return _today; }
            set { _today = value; NotifyPropertyChanged("Today"); }
        }

        private string _detailsUrl;
        /// <summary>
        /// Url to movie details page
        /// </summary>
        public string DetailsUrl
        {
            get { return _detailsUrl; }
            set { _detailsUrl = value; NotifyPropertyChanged("DetailsUrl"); }
        }

        private string _title;
        /// <summary>
        /// Movie title
        /// </summary>
        public string Title
        {
            get { return _title; }
            set { _title = value; NotifyPropertyChanged("Title"); }
        }

        private string _country;
        /// <summary>
        /// Country
        /// </summary>
        public string Country
        {
            get { return _country; }
            set { _country = value; NotifyPropertyChanged("Country"); }
        }

        private string _year;
        /// <summary>
        /// Release year
        /// </summary>
        public string Year
        {
            get { return _year; }
            set { _year = value; NotifyPropertyChanged("Year"); }
        }

        private string _director;
        /// <summary>
        /// Director
        /// </summary>
        public string Director
        {
            get { return _director; }
            set { _director = value; NotifyPropertyChanged("Director"); }
        }

        private string _genre;
        /// <summary>
        /// Genre
        /// </summary>
        public string Genre
        {
            get { return _genre; }
            set { _genre = value; NotifyPropertyChanged("Genre"); }
        }

        private string _duration;
        /// <summary>
        /// Duration in minutes
        /// </summary>
        public string Duration
        {
            get { return _duration; }
            set { _duration = value; NotifyPropertyChanged("Duration"); }
        }

        private string _restriction;
        /// <summary>
        /// Age restriction
        /// </summary>
        public string Restriction
        {
            get { return _restriction; }
            set { _restriction = value; NotifyPropertyChanged("Restriction"); }
        }

        private string _description;
        /// <summary>
        /// Movie description
        /// </summary>
        public string Description
        {
            get { return _description; }
            set { _description = value; NotifyPropertyChanged("Description"); }
        }

        private string _poster;
        /// <summary>
        /// Url to movie poster
        /// </summary>
        public string Poster
        {
            get { return _poster; }
            set { _poster = value; NotifyPropertyChanged("Poster"); }
        }

        private string _posterLocal;
        /// <summary>
        /// Cached poster
        /// </summary>
        public BitmapImage PosterLocal
        {
            get
            {
                BitmapImage temp = new BitmapImage();
                if (_posterLocal != null)
                {
                    using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
                        if (ISF.FileExists(_posterLocal))
                        {
                            using (IsolatedStorageFileStream file = ISF.OpenFile(_posterLocal, FileMode.Open, FileAccess.Read))
                                temp.SetSource(file);
                        }
                }
                return temp;
            }
        }

        private string _largePoster;
        /// <summary>
        /// Url to Large Poster
        /// </summary>
        public string LargePoster
        {
            get { return _largePoster; }
            set { _largePoster = value; }
        }

        private string _largePosterLocal;
        /// <summary>
        /// Cached Large poster
        /// </summary>
        public BitmapImage LargePosterLocal
        {
            get
            {
                BitmapImage temp = new BitmapImage();

                if (_largePosterLocal != null)
                {
                    using (IsolatedStorageFile ISF = IsolatedStorageFile.GetUserStoreForApplication())
                    using (IsolatedStorageFileStream file = ISF.OpenFile(_largePosterLocal, FileMode.Open, FileAccess.Read))
                        temp.SetSource(file);
                }
                return temp;
            }
        }

        private DateTime _airDate;
        /// <summary>
        /// Airing date
        /// </summary>
        public DateTime AirDate
        {
            get { return _airDate; }
            set { _airDate = value; NotifyPropertyChanged("AirDate"); }
        }

        private DateTime _lastDate;
        /// <summary>
        /// Last airing date
        /// </summary>
        public DateTime LastDate
        {
            get { return _lastDate; }
            set { _lastDate = value; NotifyPropertyChanged("LastDate"); }
        }

        private string _cinema;
        /// <summary>
        /// Cinema in which movie will be shown
        /// </summary>
        public string Cinema
        {
            get { return _cinema; }
            set { _cinema = value; NotifyPropertyChanged("Cinema"); }
        }

        private string _isDetailsLoaded;
        /// <summary>
        /// property to control progress bar
        /// </summary>
        public string IsDetailsLoaded
        {
            get { return _isDetailsLoaded; }
            set { _isDetailsLoaded = value; NotifyPropertyChanged("IsDetailsLoaded"); }
        }

        private string _airTimeText;

        public string AirTimeText
        {
            get { return _airTimeText; }
            set { _airTimeText = value; NotifyPropertyChanged("AirTimeText"); }
        }

        private List<AirTime> mobjAirTimes = new List<AirTime>();
        public List<AirTime> AirTimes
        {
            get { return mobjAirTimes; }
            set { mobjAirTimes = value; NotifyPropertyChanged("AirTimes"); }
        }

        private string _buyTicketUrl;

        public string BuyTicketUrl
        {
            get { return _buyTicketUrl; }
            set { _buyTicketUrl = value; NotifyPropertyChanged("BuyTicketUrl"); }
        }


        public HttpResponseMessage HtmlRequest { get; set; }

        private static CancellationTokenSource _cts = new CancellationTokenSource();

        public bool IsTaskCanceled = false;

        #endregion

        #region public async Task<bool> GetDetailsAsync() //Called for each item in MovieGroup. Downloads detailed information about movies.
        public async Task<bool> GetDetailsAsync()
        {
            if (this.Year == null)
            {

                this.IsDetailsLoaded = "visible";
                Uri uri = new Uri(this.DetailsUrl);
                HttpClient client = new HttpClient();
                HtmlDocument htmlDocument = new HtmlDocument();
                HtmlNode htmlNode = new HtmlNode(0, htmlDocument, 1);
                MovieData Item = new MovieData();
                string HtmlResult;

                try
                {
                    HtmlRequest = await client.GetAsync(uri, _cts.Token);
                    HtmlResult = await HtmlRequest.Content.ReadAsStringAsync();
                }
                catch (System.Net.Http.HttpRequestException e)
                {
                    Console.Out.WriteLine(e.Message);
                    HtmlResult = null;
                    this.IsDetailsLoaded = "Collapsed";
                }
                catch (System.Threading.Tasks.TaskCanceledException e)
                {
                    Console.Out.WriteLine(e.Message);
                    HtmlResult = null;
                    this.IsDetailsLoaded = "Collapsed";
                }

                if (!(HtmlResult == null) && HtmlRequest.IsSuccessStatusCode)
                {
                    await this.DownloadPosterAsync();
                    htmlDocument.LoadHtml(HtmlResult);
                    this.LargePoster = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='cinema_img']/a").GetAttributeValue("href", "No poster image");
                    await this.DownloadLargePosterAsync();
                    htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='cinema_desc']");
                    this.Title = GetStringFromNode(htmlNode.SelectSingleNode("h1"));
                    this.Country = GetStringFromNode(htmlNode.SelectSingleNode("p[2]"));
                    this.Year = GetStringFromNode(htmlNode.SelectSingleNode("p[3]"));
                    this.Director = GetStringFromNode(htmlNode.SelectSingleNode("p[4]"));
                    this.Genre = GetStringFromNode(htmlNode.SelectSingleNode("p[6]"));
                    this.Duration = GetStringFromNode(htmlNode.SelectSingleNode("p[7]"));
                    this.Restriction = GetStringFromNode(htmlNode.SelectSingleNode("p[8]"));
                    this.Description = GetStringFromNode(htmlNode.SelectSingleNode("p[9]/p"));
                    this.AirDate = GetAirDate(htmlNode.SelectSingleNode("p[1]"), 0);
                    this.LastDate = GetAirDate(htmlNode.SelectSingleNode("p[1]"), 19);
                    this.BuyTicketUrl = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='buy']/a").GetAttributeValue("href", "");
                    htmlNode = htmlDocument.DocumentNode.SelectSingleNode("//div[@class='sessions_day']/table/tbody");
                    if (this.Today)
                    {
                        foreach (var child in htmlNode.ChildNodes)
                        {
                            var time = new AirTime();
                            time.Time = DateTime.Parse(child.SelectSingleNode("td[1]").InnerText);
                            time.Hall = child.SelectSingleNode("td[2]").InnerText;

                            this.AirTimes.Add(time);
                            this.AirTimeText += child.SelectSingleNode("td[1]").InnerText + "  ";
                        }
                    }
                    this.IsDetailsLoaded = "Collapsed";

                }
            }
            return true;
        }
        #endregion

        #region public async Task<bool> DownloadPosterAsync() //Method for download poster images from web, save them to isolated storage with same name and then load to LLS
        public async Task<bool> DownloadPosterAsync()
        {
            WebClient client = new WebClient(); //Instantiate WebClient
            string DestFileName = "small\\" + Path.GetFileName(this.Poster);
            var isoStore = IsolatedStorageFile.GetUserStoreForApplication();
            if (!isoStore.FileExists(DestFileName))
            {
                client.OpenReadAsync(new Uri(this.Poster));

                client.OpenReadCompleted += (s, e) => //Wait for completion
                {
                    if (!isoStore.DirectoryExists("small"))
                    {
                        isoStore.CreateDirectory("small");
                    }
                    var tempBitmap = new BitmapImage(); //Create temp bitmap container
                    tempBitmap.SetSource(e.Result); //Copy stream to bitmap container
                    var writeableBitmap = new WriteableBitmap(tempBitmap); //Create WriteableBitmap out of temp bitmap
                    using (var isoFileStream = isoStore.CreateFile(DestFileName)) //Create file in Isolated Storage
                    {
                        int width = writeableBitmap.PixelWidth;//Get actual width
                        decimal i = Convert.ToDecimal(width) / 99;//Get difference between actual and desired picture size 
                        width = Convert.ToInt32(Math.Floor(width / i));//Calculate width
                        var height = Convert.ToInt32(Math.Floor(Convert.ToDecimal(writeableBitmap.PixelHeight) / i)); // Calculate Heigth
                        Extensions.SaveJpeg(writeableBitmap, isoFileStream, width, height, 0, 100); //Finally save .jpeg
                    }
                    e.Result.Close();
                };

            }
            this._posterLocal = DestFileName;
            NotifyPropertyChanged("PosterLocal");

            return true;
        }
        #endregion

        #region public async Task<bool> DownloadLargePosterAsync() //Method for download Large poster images from web, save them to isolated storage with same name and then load to LLS
        public async Task<bool> DownloadLargePosterAsync()
        {
            var isoStore = IsolatedStorageFile.GetUserStoreForApplication();
            if (!isoStore.DirectoryExists("large"))
            {
                isoStore.CreateDirectory("large");
            }
            string DestFileName = "large\\" + Path.GetFileName(this.LargePoster);
            if (!isoStore.FileExists(DestFileName))
            {
                WebClient client = new WebClient(); //Instantiate WebClient
                client.OpenReadAsync(new Uri(this.LargePoster));
                client.OpenReadCompleted += (s, e) => //Wait for completion
                {
                    var tempBitmap = new BitmapImage(); //Create temp bitmap container
                    tempBitmap.SetSource(e.Result); //Copy stream to bitmap container
                    var writeableBitmap = new WriteableBitmap(tempBitmap); //Create WriteableBitmap out of temp bitmap
                    using (var isoFileStream = isoStore.CreateFile(DestFileName)) //Create file in Isolated Storage
                    {
                        int width = writeableBitmap.PixelWidth; //Set attributes
                        decimal i = Convert.ToDecimal(width) / 400;
                        width = Convert.ToInt32(Math.Floor(width / i));
                        var height = Convert.ToInt32(Math.Floor(Convert.ToDecimal(writeableBitmap.PixelHeight) / i)); // Set some more
                        Extensions.SaveJpeg(writeableBitmap, isoFileStream, width, height, 0, 100); //Finally save .jpeg
                    }
                };
            }
            this._largePosterLocal = DestFileName;

            NotifyPropertyChanged("LargePosterLocal");

            return true;
        }
        #endregion

        #region public static void CancelAllThreads() //Does exactly what it says
        public static void CancelAllThreads()
        {
            _cts.Cancel();
            _cts = new CancellationTokenSource();
        }
        #endregion

        #region private string GetStringFromNode(HtmlNode p) //Checks if Html node not null and extracts attribute
        private string GetStringFromNode(HtmlNode p)
        {
            if (!(p == null))
            {
                string result = HttpUtility.HtmlDecode(p.InnerText);
                return result;
            }
            else
            {
                return AppResources.NoDataText;
            };
        }
        #endregion

        #region private DateTime GetAirDate(HtmlNode p, int pos) //Convert date from text representation and return first part of date string
        private DateTime GetAirDate(HtmlNode p, int pos)
        {
            //Convert this "06.02.2014 &mdash; 20.02.2014" to proper date format
            IFormatProvider culture = new System.Globalization.CultureInfo("ru-RU");
            string cultureName = Thread.CurrentThread.CurrentCulture.Name;
            if (!(p == null))
            {
                string temp = p.InnerText.Substring(pos, 10);
                temp = temp.Replace(".", "/");

                DateTime result = DateTime.Parse(temp, culture, System.Globalization.DateTimeStyles.AssumeLocal);
                return result;
            }
            else
            {
                return DateTime.Now;
            }

        }
        #endregion

        #region public void NotifyPropertyChanged(string info) //Update info at controls
        public event PropertyChangedEventHandler PropertyChanged;
        public void NotifyPropertyChanged(string info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }
        #endregion


    }

}
